package com.viknix.threadpool.manager.client.message;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.dingtalk.api.DefaultDingTalkClient;
import com.dingtalk.api.DingTalkClient;
import com.dingtalk.api.request.OapiRobotSendRequest;
import com.google.common.base.Joiner;
import com.taobao.api.ApiException;
import com.viknix.threadpool.manager.client.core.DynamicThreadPoolExecutor;
import com.viknix.threadpool.manager.client.core.DynamicThreadPoolWrap;
import com.viknix.threadpool.manager.client.factory.RejectedHandlerFactory;
import com.viknix.threadpool.manager.client.global.GlobalThreadPoolManage;
import com.viknix.threadpool.manager.client.enums.SendMessageEnum;
import com.viknix.threadpool.manager.client.model.InstanceInfo;
import com.viknix.threadpool.manager.common.model.PoolParameterInfo;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @Author: Dongqi
 * @Date: 2021/10/23 15:41
 * @Version 1.0
 * @Description: 消息处理器
 */
@Slf4j
@RequiredArgsConstructor
public class DingSendMessageHandler implements SendMessageHandler {

    @NonNull
    private InstanceInfo instanceInfo;

    @NonNull
    private String active;

    @NonNull
    private Long period;

    @Override
    public String getType() {
        return SendMessageEnum.DING.name();
    }

    @Override
    public void sendAlertorMessage(List<MessageReceiver> messageReceivers, DynamicThreadPoolExecutor pool) {
        List<MessageReceiver> messageReceiverList = messageReceivers.stream()
                .filter(each -> Objects.equals(each.getType(), getType()))
                .collect(Collectors.toList());
        if (messageReceiverList != null && messageReceiverList.size() > 0) {
            messageReceiverList.stream().forEach(each -> dingAlertorSendMessage(each, pool));
        }
    }

    @Override
    public void sendChangeMessage(List<MessageReceiver> alarmConfigs, PoolParameterInfo newParameter, PoolParameterInfo oldParameter) {
        Optional<MessageReceiver> changeConfigOptional = alarmConfigs.stream()
                .filter(each -> Objects.equals(each.getType(), getType()))
                .findFirst();
        changeConfigOptional.ifPresent(each -> dingChangeSendMessage(each, newParameter, oldParameter));
    }

    private void dingAlertorSendMessage(MessageReceiver messageReceiver, DynamicThreadPoolExecutor pool) {

        List telList = Optional.ofNullable(messageReceiver.getTel()).map(tel -> StrUtil.split(tel, ',')).orElse(Collections.EMPTY_LIST);
        String tel = Joiner.on(", @").join(telList);

        BlockingQueue<Runnable> queue = pool.getQueue();
        String text = String.format(
                "<font color='#FF0000'>[警报] </font>%s - 动态线程池运行告警 \n\n" +
                        " --- \n\n " +
                        "<font color='#708090' size=2>线程池ID：%s</font> \n\n " +
                        "<font color='#778899' size=2>应用实例：%s</font> \n\n " +
                        " --- \n\n  " +
                        "<font color='#708090' size=2>核心线程数：%d</font> \n\n " +
                        "<font color='#708090' size=2>最大线程数：%d</font> \n\n " +
                        "<font color='#708090' size=2>当前线程数：%d</font> \n\n " +
                        "<font color='#708090' size=2>活跃线程数：%d</font> \n\n " +
                        "<font color='#708090' size=2>最大线程数：%d</font> \n\n " +
                        "<font color='#708090' size=2>线程池任务总量：%d</font> \n\n " +
                        " --- \n\n  " +
                        "<font color='#708090' size=2>队列类型：%s</font> \n\n " +
                        "<font color='#708090' size=2>队列容量：%d</font> \n\n " +
                        "<font color='#708090' size=2>队列元素个数：%d</font> \n\n " +
                        "<font color='#708090' size=2>队列剩余个数：%d</font> \n\n " +
                        " --- \n\n  " +
                        "<font color='#708090' size=2>拒绝策略：%s</font> \n\n" +
                        "<font color='#708090' size=2>拒绝策略执行次数：</font><font color='#FF0000' size=2>%d</font> \n\n " +
                        "<font color='#708090' size=2>OWNER：@%s</font> \n\n" +
                        "<font color='#708090' size=2>提示：%d 分钟内此线程池不会重复告警（可配置）</font> \n\n" +
                        " --- \n\n  " +
                        "**播报时间：%s**",

                // 环境
                active.toUpperCase(),
                // 线程池ID
                pool.getThreadPoolId(),
                // 节点信息
                instanceInfo.getIpApplicationName(),
                // 核心线程数
                pool.getCorePoolSize(),
                // 最大线程数
                pool.getMaximumPoolSize(),
                // 当前线程数
                pool.getPoolSize(),
                // 活跃线程数
                pool.getActiveCount(),
                // 最大任务数
                pool.getLargestPoolSize(),
                // 线程池任务总量
                pool.getCompletedTaskCount(),
                // 队列类型名称
                queue.getClass().getSimpleName(),
                // 队列容量
                queue.size() + queue.remainingCapacity(),
                // 队列元素个数
                queue.size(),
                // 队列剩余个数
                queue.remainingCapacity(),
                // 拒绝策略名称
                pool.getRejectedExecutionHandler().getClass().getSimpleName(),
                // 拒绝策略次数
                pool.getRejectCount(),
                // 告警手机号
                tel,
                // 告警间隔时间
                period,
                // 当前时间
                DateUtil.now()
        );

        execute(messageReceiver, "动态线程池告警", text, telList);
    }

    private void dingChangeSendMessage(MessageReceiver messageReceiver, PoolParameterInfo newParameter, PoolParameterInfo oldParameter) {

        List<String> telList = Optional.ofNullable(messageReceiver.getTel()).map(tel -> StrUtil.split(tel, ',')).orElse(Collections.EMPTY_LIST);
        String tel = Joiner.on(", @").join(telList);

        String threadPoolId = newParameter.getThreadPoolId();

        // 从全局管理器中获取线程池对象
        DynamicThreadPoolWrap poolWrap = GlobalThreadPoolManage.getExecutorService(threadPoolId);
        // 如果没有该对象直接返回
        if (poolWrap == null) {
            log.warn("Thread pool is empty when sending change notification, threadPoolId :: {}", threadPoolId);
            return;
        }
        DynamicThreadPoolExecutor customPool = poolWrap.getThreadPool();

        /**
         * hesitant e.g. ➲  ➜  ⇨  ➪
         */
        String text = String.format(
                "<font color='#2a9d8f'>[通知] </font>%s - 动态线程池参数变更 \n\n" +
                        " --- \n\n " +
                        "<font color='#708090' size=2>线程池ID：%s</font> \n\n " +
                        "<font color='#778899' size=2>应用实例：%s</font> \n\n " +
                        " --- \n\n  " +
                        "<font color='#708090' size=2>核心线程数：%s</font> \n\n " +
                        "<font color='#708090' size=2>最大线程数：%s</font> \n\n " +
                        "<font color='#708090' size=2>线程存活时间：%s / SECONDS</font> \n\n" +
                        " --- \n\n  " +
                        "<font color='#708090' size=2>队列类型：%s</font> \n\n " +
                        "<font color='#708090' size=2>队列容量：%s</font> \n\n " +
                        " --- \n\n  " +
                        "<font color='#708090' size=2>AGO 拒绝策略：%s</font> \n\n" +
                        "<font color='#708090' size=2>NOW 拒绝策略：%s</font> \n\n" +
                        " --- \n\n  " +
                        "<font color='#708090' size=2>提示：动态线程池配置变更实时通知（无限制）</font> \n\n" +
                        "<font color='#708090' size=2>OWNER：@%s</font> \n\n" +
                        " --- \n\n  " +
                        "**播报时间：%s**",
                // 环境
                active.toUpperCase(),
                // 线程池名称
                threadPoolId,
                // 节点信息
                instanceInfo.getIpApplicationName(),
                // 核心线程数
                oldParameter.getCorePoolSize() + "  ➲  " + newParameter.getCorePoolSize(),
                // 最大线程数
                oldParameter.getMaxPoolSize() + "  ➲  " + newParameter.getMaxPoolSize(),
                // 线程存活时间
                oldParameter.getKeepAliveTime() + "  ➲  " + newParameter.getKeepAliveTime(),
                // 阻塞队列名称
                customPool.getQueue().getClass().getSimpleName(),
                // 阻塞队列容量
                oldParameter.getCapacity() + "  ➲  " + newParameter.getCapacity(),
                // 拒绝策略
                RejectedHandlerFactory.getNameByCode(oldParameter.getRejectedType()),
                // 拒绝策略名称
                RejectedHandlerFactory.getNameByCode(newParameter.getRejectedType()),
                // 需要通知的人员
                tel,
                // 当前时间
                DateUtil.now()
        );

        execute(messageReceiver, "动态线程池通知", text, telList);
    }

    private void execute(MessageReceiver messageReceiver, String title, String text, List<String> mobiles) {

        String serverUrl = messageReceiver.getUrl() + messageReceiver.getToken();
        DingTalkClient dingTalkClient = new DefaultDingTalkClient(serverUrl);
        OapiRobotSendRequest request = new OapiRobotSendRequest();
        request.setMsgtype("markdown");

        OapiRobotSendRequest.Markdown markdown = new OapiRobotSendRequest.Markdown();
        markdown.setTitle(title);
        markdown.setText(text);

        OapiRobotSendRequest.At at = new OapiRobotSendRequest.At();
        at.setAtMobiles(mobiles);

        request.setAt(at);
        request.setMarkdown(markdown);

        try {
            dingTalkClient.execute(request);
        } catch (ApiException ex) {
            log.error("Ding failed to send message", ex.getMessage());
        }
    }

}
